UNPKG

studiocms

Version:

Astro Native CMS for AstroDB. Built from the ground up by the Astro community.

287 lines (286 loc) 9.56 kB
import { User } from "studiocms:auth/lib"; import { apiResponseLogger } from "studiocms:logger"; import { Notifications } from "studiocms:notifier"; import { SDKCore } from "studiocms:sdk"; import { UserPermissionLevel } from "@withstudiocms/auth-kit/types"; import { AllResponse, createEffectAPIRoutes, createJsonResponse, Effect, genLogger, OptionsResponse, parseAPIContextJson, Schema } from "../../../../../../effect.js"; import { verifyAuthTokenFromHeader } from "../../utils/auth-token.js"; class JSONData extends Schema.Class("JSONData")({ rank: Schema.Union( Schema.Literal("owner"), Schema.Literal("admin"), Schema.Literal("editor"), Schema.Literal("visitor"), Schema.Literal("unknown") ) }) { } const { ALL, DELETE, GET, OPTIONS, PATCH } = createEffectAPIRoutes( { GET: (ctx) => genLogger("studioCMS:rest:v1:users:[id]:GET")(function* () { const [sdk, user, userUtils] = yield* Effect.all([ SDKCore, verifyAuthTokenFromHeader(ctx), User ]); if (user instanceof Response) { return user; } const { rank } = user; if (rank !== "owner" && rank !== "admin") { return apiResponseLogger(401, "Unauthorized"); } const { id } = ctx.params; if (!id) { return apiResponseLogger(400, "Invalid form data, id is required"); } const existingUser = yield* sdk.GET.users.byId(id); if (!existingUser) { return apiResponseLogger(404, "User not found"); } const { avatar, createdAt, email, name, permissionsData, updatedAt, url, username } = existingUser; const existingUserRank = permissionsData?.rank ?? "visitor"; const data = { avatar, createdAt, email, id, name, rank: existingUserRank, updatedAt, url, username }; const loggedInUser = yield* sdk.GET.users.byId(user.userId); if (!loggedInUser || loggedInUser === void 0) { return apiResponseLogger(400, "User Error"); } const permissionLevelInput = { isLoggedIn: true, user: loggedInUser, permissionLevel: loggedInUser.permissionsData?.rank ?? "visitor" }; const userPermissionLevel = yield* userUtils.getUserPermissionLevel(permissionLevelInput); const requiredPerms = () => { switch (existingUserRank) { case "owner": return UserPermissionLevel.owner; case "admin": return UserPermissionLevel.admin; case "editor": return UserPermissionLevel.editor; case "visitor": return UserPermissionLevel.visitor; default: return UserPermissionLevel.unknown; } }; const isAllowed = userPermissionLevel > requiredPerms(); if (!isAllowed) { return apiResponseLogger(401, "Unauthorized"); } return createJsonResponse(data); }), PATCH: (ctx) => genLogger("studioCMS:rest:v1:users:[id]:PATCH")(function* () { const [sdk, user, userUtils, notifier] = yield* Effect.all([ SDKCore, verifyAuthTokenFromHeader(ctx), User, Notifications ]); if (user instanceof Response) { return user; } const { rank } = user; if (rank !== "owner" && rank !== "admin") { return apiResponseLogger(401, "Unauthorized"); } const { id } = ctx.params; if (!id) { return apiResponseLogger(400, "Invalid form data, id is required"); } const existingUser = yield* sdk.GET.users.byId(id); if (!existingUser) { return apiResponseLogger(400, "User not found"); } const { permissionsData } = existingUser; const existingUserRank = permissionsData?.rank ?? "admin"; const loggedInUser = yield* sdk.GET.users.byId(user.userId); if (!loggedInUser || loggedInUser === void 0) { return apiResponseLogger(400, "User Error"); } const permissionLevelInput = { isLoggedIn: true, user: loggedInUser, permissionLevel: loggedInUser.permissionsData?.rank ?? "visitor" }; const userPermissionLevel = yield* userUtils.getUserPermissionLevel(permissionLevelInput); const requiredPerms = () => { switch (existingUserRank) { case "owner": return UserPermissionLevel.owner; case "admin": return UserPermissionLevel.admin; case "editor": return UserPermissionLevel.editor; case "visitor": return UserPermissionLevel.visitor; default: return UserPermissionLevel.unknown; } }; const isAllowed = userPermissionLevel > requiredPerms(); if (!isAllowed) { return apiResponseLogger(401, "Unauthorized"); } const { rank: newRank } = yield* parseAPIContextJson(ctx, JSONData); if (!newRank) { return apiResponseLogger(400, "Missing field: Rank is required"); } const requiredPermsForNewRank = (() => { switch (newRank) { case "owner": return UserPermissionLevel.owner; case "admin": return UserPermissionLevel.admin; case "editor": return UserPermissionLevel.editor; case "visitor": return UserPermissionLevel.visitor; default: return UserPermissionLevel.unknown; } })(); if (userPermissionLevel <= requiredPermsForNewRank) { return apiResponseLogger(403, "Forbidden"); } const updateRank = yield* sdk.UPDATE.permissions({ user: id, rank: newRank }); if (!updateRank) { return apiResponseLogger(400, "Failed to update rank"); } const updatedUser = yield* sdk.GET.users.byId(id); if (!updatedUser) { return apiResponseLogger(400, "Failed to get updated user"); } const { avatar, createdAt, email, name, permissionsData: newPermissionsData, updatedAt, url, username } = updatedUser; const updatedUserRank = newPermissionsData?.rank ?? "unknown"; const data = { avatar, createdAt, email, id, name, rank: updatedUserRank, updatedAt, url, username }; yield* notifier.sendUserNotification("account_updated", id); yield* notifier.sendAdminNotification("user_updated", username); return createJsonResponse(data); }).pipe(Notifications.Provide), DELETE: (ctx) => genLogger("studioCMS:rest:v1:users:[id]:DELETE")(function* () { const [sdk, user, userUtils, notifier] = yield* Effect.all([ SDKCore, verifyAuthTokenFromHeader(ctx), User, Notifications ]); if (user instanceof Response) { return user; } const { rank } = user; if (rank !== "owner" && rank !== "admin") { return apiResponseLogger(401, "Unauthorized"); } const { id } = ctx.params; if (!id) { return apiResponseLogger(400, "Invalid form data, id is required"); } if (id === user.userId) { return apiResponseLogger(400, "Cannot delete your own account"); } const existingUser = yield* sdk.GET.users.byId(id); if (!existingUser) { return apiResponseLogger(400, "User not found"); } const { permissionsData } = existingUser; const existingUserRank = permissionsData?.rank ?? "admin"; const loggedInUser = yield* sdk.GET.users.byId(user.userId); if (!loggedInUser || loggedInUser === void 0) { return apiResponseLogger(400, "User Error"); } const permissionLevelInput = { isLoggedIn: true, user: loggedInUser, permissionLevel: loggedInUser.permissionsData?.rank ?? "visitor" }; const userPermissionLevel = yield* userUtils.getUserPermissionLevel(permissionLevelInput); const requiredPerms = () => { switch (existingUserRank) { case "owner": return UserPermissionLevel.owner; case "admin": return UserPermissionLevel.admin; case "editor": return UserPermissionLevel.editor; case "visitor": return UserPermissionLevel.visitor; default: return UserPermissionLevel.unknown; } }; const isAllowed = userPermissionLevel > requiredPerms(); if (!isAllowed) { return apiResponseLogger(401, "Unauthorized"); } const response = yield* sdk.DELETE.user(id); if (!response) { return apiResponseLogger(400, "Failed to delete user"); } if (response.status === "error") { return apiResponseLogger(400, response.message); } yield* notifier.sendAdminNotification("user_deleted", existingUser.username); return apiResponseLogger(200, response.message); }).pipe(Notifications.Provide), OPTIONS: () => Effect.try(() => OptionsResponse({ allowedMethods: ["GET", "PATCH", "DELETE"] })), ALL: () => Effect.try(() => AllResponse()) }, { cors: { methods: ["GET", "PATCH", "DELETE", "OPTIONS"] }, onError: (error) => { console.error("API Error:", error); return createJsonResponse({ error: "Something went wrong" }, { status: 500 }); } } ); export { ALL, DELETE, GET, JSONData, OPTIONS, PATCH };